#include "FunctionContext.h"
#include "AnalyzeTraversal.h"

FunctionContext::FunctionContext(SgFunctionDeclaration *set_decl, analyzeTraversal *set_all) {
    all = set_all;
    decl = set_decl;
    function_calls = NULL;
    local_variables = NULL;
    callers = NULL;
    calls_this = NULL;
    parameters = NULL;
    n_calls = 0;
    n_local_vars = 0;
    n_callers = 0;
    function_confirmed = 0;
    int n_args = decl->get_args().size();
    SgInitializedNamePtrList& args = decl->get_args(); 
    parameters = (VariableWrapper **)malloc(sizeof(VariableWrapper *) * n_args);
    for(int i = 0; i < n_args; i++) {
        SgInitializedName *name = isSgInitializedName(args[i]);
        if(name != NULL) {
            parameters[i] = new VariableWrapper(name, set_decl, this);
        } else {
            parameters[i] = NULL;
        }
    }
    writes = NULL;
    n_writes = 0;

    reads = NULL;
    n_reads = 0;
}

int FunctionContext::contains(Reference **arr, Reference *ref, int N) {
    for(int i = 0 ;i < N; i++) {
        if(arr[i]->equals(ref)) {
            return 1;
        }
    }
    return 0;
}

void FunctionContext::appendReads(Reference **refs, int n) {
    for(int i = 0 ; i < n ; i ++) {
        if(contains(reads, refs[i], n_reads) == 0) {
            reads = (Reference **)realloc(reads, sizeof(Reference *) * (n+1));
            reads[n_reads] = refs[i];
            n_reads++;
        }
    }
}

void FunctionContext::addCall(FullFunctionCall *call) {
    function_calls = (FullFunctionCall **)realloc(function_calls, (n_calls+1) * sizeof(FullFunctionCall *));
    function_calls[n_calls] = call;
    n_calls = n_calls + 1;
}

VariableWrapper *FunctionContext::addLocal(SgVariableDeclaration *local) {

    SgInitializedNamePtrList& all_vars = local->get_variables();
    for(unsigned int k = 0; k < all_vars.size(); k++) {
        local_variables = (VariableWrapper **)realloc(local_variables, sizeof(VariableWrapper *) * (n_local_vars +1));
        local_variables[n_local_vars] = new VariableWrapper(all_vars[k], local, this);
        n_local_vars = n_local_vars + 1;
    }
    return local_variables[n_local_vars-1];
}

void FunctionContext::addCaller(FunctionContext *caller,FullFunctionCall *call) {
    callers = (FunctionContext **)realloc(callers, sizeof(FunctionContext *) * (n_callers+1));
    calls_this = (FullFunctionCall **)realloc(calls_this, sizeof(FullFunctionCall *) * (n_callers+1));
    callers[n_callers] = caller;
    calls_this[n_callers] = call;
    n_callers++;

    SgFunctionCallExp *actual = call->getCall();
    SgExprListExp *args = actual->get_args();
    SgExpressionPtrList& expressions = args->get_expressions();
    int n_args =expressions.size();
    for(int i = 0; i < n_args; i++) {
        if(parameters[i] == NULL) continue;
        if((call->get_vars())[i] == NULL) continue;
        parameters[i]->addAlias((call->get_refs())[i]);
    }
}

void FunctionContext::appendWrites(Reference **refs, int n) {
    for(int i = 0; i < n; i++)  {
        if(contains(writes, refs[i], n_writes) == 0) {
            writes = (Reference **)realloc(writes, sizeof(Reference *) * (n_writes+1));
            writes[n_writes] = refs[i];
            n_writes++;
        }
    }
}

string FunctionContext::str() {
    stringstream output;

    output<<"Function "<<decl->get_name().str()<<" makes "<<n_calls<<" calls, takes "<<decl->get_args().size()<<" arguments"<< \
        " and has "<<n_local_vars<<" local variables"<<endl;
    if(n_calls > 0) {
        output<<"    Calls("<<n_calls<<"): ";
        output<<function_calls[0]->getCall()->getAssociatedFunctionDeclaration()->get_name().str();
        for(int j = 1; j < n_calls; j++) {
            output<<", "<<function_calls[j]->getCall()->getAssociatedFunctionDeclaration()->get_name().str();
            //SgExprListExp *args = function_calls[j]->getCall()->get_args();
            //SgExpressionPtrList& exprs = args->get_expressions();
            /*
            VariableWrapper **args = function_calls[j]->get_vars();
            int n_args = function_calls[j]->num_args();
            int n_nonnull = 0;
            for(int k = 0; k < n_args; k++) {
                if(args[k] != NULL) n_nonnull++;
            }
            output<<"            Takes "<<n_args<<", "<<n_nonnull<<" args"<<endl;
            */
            /*
            for(unsigned int k = 0; k < exprs.size(); k++) {
                SgExpression *arg = exprs[k];
                SgVarRefExp *var = isSgVarRefExp(arg);

                SgDeclarationStatement **args_to_call = function_calls[j]->getArgsPassed();
                if(var != NULL) {
                    output<<"            Takes "<<var->get_symbol()->get_name().str()<<endl;
                }
                if(args_to_call[k] != NULL) {
                    SgVariableDeclaration *var_decl = isSgVariableDeclaration(args_to_call[k]);
                    output<<"                something "<<args_to_call[k]->variantT()<<endl;
                    if(var_decl != NULL) {
                        SgType *type = var_decl->get_definition()->get_type();
                        Argument *myType = translateArgs(type);
                        output<<"                of type "<<myType->typeStr()<<endl;
                    }
                }
                //cout<<"            of node type "<<arg->variantT()<<endl;
            }
            */
        }
        output<<endl;
    }

    if(decl->get_args().size() > 0) {
        output<<"    Arguments("<<decl->get_args().size()<<"):"<<endl;
        SgInitializedNamePtrList& args = decl->get_args();
        for(unsigned int j = 0; j < args.size(); j++) {
            VariableWrapper *arg = parameters[j];
            output<<"        "<<arg->full_str()<<" with "<<parameters[j]->num_aliases()<<" aliases";
            if(arg->num_aliases() > 0) {
                output<<" (";
                output<<arg->ithAlias(0)->full_str();
                for(int k = 1; k < arg->num_aliases(); k++) {
                    output<<", "<<arg->ithAlias(k)->full_str();
                }
                output<<")";
            }
            output<<endl;
            /*
            SgType *type = args[j]->get_typeptr();
            SgName name = args[j]->get_name().str();
            Argument *myArg = translateArgs(type);
            output<<"        Arg "<<j<<" of type "<<myArg->typeStr()<<" called "<<name.str()<<" with "<<parameters[j]->num_aliases()<<" aliases";
            
            if(parameters[j]->num_aliases() > 0) {
                output<<" (";
                output<<parameters[j]->ithAlias(0)->full_str();
                for(int k = 1; k < parameters[j]->num_aliases(); k++) {
                    output<<", "<<parameters[j]->ithAlias(k)->full_str();
                }
                output<<")";
            }
            
            output<<endl;
            */
        }
    }
       
    if(n_local_vars > 0) {
        output<<"    Locals("<<n_local_vars<<"): ";
        VariableWrapper *local = local_variables[0];
        SgType *type = local->get_type();
        Argument *myType = translateArgs(type);
        output<<myType->typeStr()<<" "<<local->str();
        for(int j = 1; j < n_local_vars; j++) {
            local = local_variables[j];
            type = local->get_type();
            myType = translateArgs(type);
            output<<", "<<myType->typeStr()<<" "<<local->str();
        }
        output<<endl;
    }
    
    if(n_writes > 0) {
        output<<"    Writes("<<n_writes<<"):"<<endl;
        output<<"        "<<writes[0]->full_str()<<endl;
        for(int i = 1; i < n_writes; i++) {
            Reference *r = writes[i];
            output<<"        "<<r->full_str()<<endl;
        }
    }
    
    
    if(n_reads > 0) {
        output<<"    Reads("<<n_reads<<"):"<<endl;
        output<<"        "<<reads[0]->full_str()<<endl;
        for(int i = 1; i < n_reads; i++) {
            Reference *r = reads[i];
            output<<"        "<<r->full_str()<<endl;
        }
    }
    
    return output.str();
}

string FunctionContext::get_name() {
    return decl->get_name().str();
}

SgFunctionDeclaration *FunctionContext::get_decl() {
    return decl;
}

void FunctionContext::confirmFunction() {
    function_confirmed = 1;
}

int FunctionContext::isConfirmed() {
    return function_confirmed;
}

VariableWrapper *FunctionContext::verySimpleResolve(string name) {
    int n_args = decl->get_args().size();
    for(int i = 0; i < n_args; i++) {
        if(parameters[i]->str() == name) {
            return parameters[i];
        }
    }
    for(int i = 0; i < n_local_vars; i++) {
        if(local_variables[i]->str() == name) {
            return local_variables[i];
        }
    }
    for(int i = 0; i < all->num_globals(); i++) {
        if(all->get_globals()[i]->str() == name) {
            return all->get_globals()[i];
        }
    }
    return NULL;
}


VariableWrapper *FunctionContext::findVarByName(string name) {

    for(int i = n_local_vars-1; i >= 0; i--) {
        if(local_variables[i]->str() == name) return local_variables[i];
    }

    int n_args = this->decl->get_args().size();
    for(int i = 0; i < n_args; i++) {
        if(parameters[i]->str() == name) {
            return parameters[i];
        }
    }
    for(int i = 0; i < all->num_globals(); i++) {
        if(all->get_globals()[i]->str() == name) {
            return all->get_globals()[i];
        }
    }
    return NULL;
}

VariableWrapper *FunctionContext::findVarByDecl(SgDeclarationStatement *decl) {
    int n_args = this->decl->get_args().size();
    for(int i = 0; i < n_args; i++) {
        if(parameters[i]->get_decl() == decl) {
            return parameters[i];
        }
    }
    for(int i = 0; i < n_local_vars; i++) {
        if(local_variables[i]->get_decl() == decl) {
            return local_variables[i];
        }
    }
    for(int i = 0; i < all->num_globals(); i++) {
        if(all->get_globals()[i]->get_decl() == decl) {
            return all->get_globals()[i];
        }
    }
    return NULL;
}

int FunctionContext::n_args() {
    return decl->get_args().size();
}

